// Copyright (c) 2020-present, INSPUR Co, Ltd. All rights reserved.
// This source code is licensed under Apache 2.0 License.
//
// we use Epoche class to manage the deleting objects in memroy.
// in current, we use strategy: delay 3 seconds then actually delete from memroy.
// just make sure that: all deleting object be on longer used. 

#pragma once
#include <atomic>
#include <array>
#include <mutex>
#include <queue>
#include <thread>
#include "version_node.h"

namespace rocksdb {
    // Epoche class manage kinds of object, each type with node_type.
    enum NODE_TYPE{
        DELETION_TYPE_ART = 0,
        DELETION_TYPE_VERSION = 1,
        DELETION_TYPE_POINTER = 2
    };
    // LabelDelete class store deleting object info.
    class LabelDelete {
    public:
        void* node_;
        NODE_TYPE type_;
        struct timespec timestamp_;

        LabelDelete(void *n, NODE_TYPE type);
        void clone(const LabelDelete& other);
        void delLabel();
    };

    class DeletionList {
        std::mutex mutex_;
        std::queue<LabelDelete> wait4freeList_;

    public:
        ~DeletionList(){}

        void add(void *n, NODE_TYPE type);
        LabelDelete removeLast();
        bool empty() const;
        size_t size() const{
            return wait4freeList_.size();
        }
    };
    // DeleteWhileNoRefs class manage all deleting object.
    // use singleton pattern make sure current program just has one instance.
    class DeleteWhileNoRefs {
        DeletionList deleteList_;
        bool closed_ = false;
        size_t startGCThreshhold_ = 3; // actual delete after this seconds.
        std::thread clean_;
        static DeleteWhileNoRefs* ins_;

        DeleteWhileNoRefs();

    public:
        void setGCThreshold(size_t gct){
            startGCThreshhold_ = gct;
        }
        static DeleteWhileNoRefs* getInstance(){
            if (ins_ == nullptr){
                ins_ = new DeleteWhileNoRefs();
            }
            return ins_;
        }

        void markNodeForDeletion(void *n, NODE_TYPE type=DELETION_TYPE_ART);
        size_t getDeleteNum() const { return deleteList_.size();}
        ~DeleteWhileNoRefs();
        void cleanup();
    };
}
